import java.awt.Container;
import java.awt.EventQueue;
import java.awt.FileDialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Scanner;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SpringLayout;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class InputFrame extends JFrame {
	private static final long serialVersionUID = 1L;

	private JTable table;
	private MyTable tableModel;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable() {
			public void run()
			{
				try
				{
					InputFrame frame = new InputFrame();
					frame.setVisible(true);
				} catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 * @throws UnsupportedLookAndFeelException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws ClassNotFoundException 
	 */
	public InputFrame() throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
	{
		UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 592, 494);
		Container content = getContentPane();

		SpringLayout springLayout = new SpringLayout();
		content.setLayout(springLayout);

		String[] headers = { "State", "0", "1" };
		tableModel = new MyTable(new String[3][3], headers);

		JScrollPane scrollPane = new JScrollPane();
		content.add(scrollPane);

		table = new JTable(tableModel);
		scrollPane.setViewportView(table);
		springLayout.putConstraint(SpringLayout.NORTH, scrollPane, 20,
				SpringLayout.NORTH, content);
		springLayout.putConstraint(SpringLayout.WEST, scrollPane, 0,
				SpringLayout.WEST, content);
		springLayout.putConstraint(SpringLayout.SOUTH, scrollPane, 0,
				SpringLayout.SOUTH, content);
		springLayout.putConstraint(SpringLayout.EAST, scrollPane, 0,
				SpringLayout.EAST, content);

		JMenuBar menuBar = new JMenuBar();
		springLayout.putConstraint(SpringLayout.WEST, menuBar, 0, SpringLayout.WEST, getContentPane());
		springLayout.putConstraint(SpringLayout.EAST, menuBar, 510, SpringLayout.EAST, getContentPane());
		content.add(menuBar);

		JMenu editMenu = new JMenu("Edit");
		menuBar.add(editMenu);

		JMenuItem file = new JMenuItem("Open File");
		editMenu.add(file);
		file.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				readFile();
			}
		});

		JMenuItem setStates = new JMenuItem("Set number of States");
		editMenu.add(setStates);
		setStates.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				setStates();
			}
		});

		JMenuItem exit = new JMenuItem("Exit");
		editMenu.add(exit);
		exit.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				System.exit(0);
			}
		});

		JMenu solveMenu = new JMenu("Solve");
		menuBar.add(solveMenu);

		JMenuItem simple = new JMenuItem("Simple Control");
		solveMenu.add(simple);
		simple.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e)
			{
				try {
					solveSimpleControl();
				} catch (ClassNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (InstantiationException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (IllegalAccessException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (UnsupportedLookAndFeelException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});

		JMenuItem terminalState = new JMenuItem("Terminal State Identification");
		solveMenu.add(terminalState);
		terminalState.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e)
			{
				try {
					solveTerminal();
				} catch (ClassNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (InstantiationException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (IllegalAccessException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (UnsupportedLookAndFeelException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});

		JMenuItem terminalSynch = new JMenuItem("Terminal State Synch.");
		solveMenu.add(terminalSynch);
		terminalSynch.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e)
			{
				try {
					solveSynch();
				} catch (ClassNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (InstantiationException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (IllegalAccessException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (UnsupportedLookAndFeelException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});

		JMenuItem generalControl = new JMenuItem("General Control");
		solveMenu.add(generalControl);
		generalControl.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e)
			{
				try {
					solveGeneralControl();
				} catch (ClassNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (InstantiationException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (IllegalAccessException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (UnsupportedLookAndFeelException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
	}

	private void readFile()
	{
		FileDialog fd = new FileDialog(new JFrame(), "Open input File",
				FileDialog.LOAD);
		fd.setVisible(true);
		String file = fd.getFile();
		String dir = fd.getDirectory();
		boolean ok = true;
		if (file != null)
		{
			String[][] d = null;
			try
			{
				Scanner scan = new Scanner(new File(dir + file));
				Scanner in;
				LinkedList<String> lines = new LinkedList<String>();
				while (scan.hasNextLine())
				{
					lines.add(scan.nextLine());
				}
				d = new String[lines.size()][3];
				int pos = 0;
				for (String line : lines)
				{
					in = new Scanner(line);
					d[pos][0] = in.next();
					d[pos][1] = in.next();
					d[pos++][2] = in.next();
					if (in.hasNext())
					{
						ok = false;
						break;
					}
				}
			} catch (Exception e)
			{
				ok = false;
			}
			if (!ok)
				JOptionPane.showMessageDialog(null, "Invalid File Format",
						"Error", JOptionPane.ERROR_MESSAGE);
			else
			{
				tableModel.setTable(d);
				repaint();
			}
		}
	}

	private void setStates()
	{
		int t = 0;
		do
		{
			String input = JOptionPane
					.showInputDialog("Enter the Number of States");
			if (input != null && !input.isEmpty())
			{
				try
				{
					t = Integer.parseInt(input);
				} catch (Exception e)
				{
					JOptionPane.showMessageDialog(null,
							"Invalid Number of States", "Error",
							JOptionPane.ERROR_MESSAGE);
				}
			} else
				break;
		} while (t < 1);
		if (t > 1)
		{
			tableModel.setTable(new String[t][3]);
			repaint();
		}
	}

	private Hashtable<String, Integer> hash;
	private int[][] transitionTable;
	private int[][] output;
	private String[] states;
	private OutputFrame outFrame;

	/**
	 * this method to translate the input data to nextState table and output
	 * table
	 * 
	 * @return boolean whether the data is valid or not
	 */
	private boolean readInput()
	{
		String[][] input = tableModel.getData();

		hash = new Hashtable<String, Integer>(input.length);
		transitionTable = new int[input.length][2];
		output = new int[input.length][2];
		states = new String[input.length];

		// First will read the states and number them
		// hash will indicate its number
		// can get the states with any number from the states[]

		// ------------------------------------------
		// For Clarity
		// String -> Integer use hash
		// Integer -> String use states[]
		// --------------------------------------------
		for (int i = 0; i < input.length; i++)
		{
			String s = input[i][0];
			if (hash.containsKey(s))
			{
				return false;
			}
			hash.put(s, i);
			states[i] = s;
		}

		// Will read transition table and the output
		for (int i = 0; i < input.length; i++)
		{
			boolean valid = readCell(input[i][1], i, 0);
			if (!valid)
				return false;

			valid = readCell(input[i][2], i, 1);
			if (!valid)
				return false;
		}
		return true;
	}

	/**
	 * this method to read a cell of input
	 * 
	 * @param s
	 *            the cell
	 * @param i
	 *            the number of the row (the state)
	 * @param input
	 *            the input that was applied for such transition
	 * @return if the cell has valid format or not
	 */
	private boolean readCell(String s, int i, int input)
	{
		String[] a = s.split(",");
		if (a.length < 2)
		{
			return false;

		}
		Integer index = hash.get(a[0]);

		if (index == null
				|| (a[1].compareTo("0") != 0 && a[1].compareTo("1") != 0))
		{
			// Not a Valid State or output
			return false;
		}
		transitionTable[i][input] = index;
		output[i][input] = a[1].charAt(0) - '0';
		return true;
	}

	private void solveSimpleControl() throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
	{
		boolean valid = readInput();
		if (!valid)
		{
			JOptionPane.showMessageDialog(null, "Invalid Number of States",
					"Error", JOptionPane.ERROR_MESSAGE);
			return;
		}
		TreeBuilder tree = new TreeBuilder(transitionTable, output);
		Integer start = null;
		do
		{
			String s1 = JOptionPane.showInputDialog("Enter Initial State");

			if (s1 == null)
				start = -1;
			else
				start = hash.get(s1);
		} while (start == null);

		if (start >= 0)
		{
			Integer end = null;
			do
			{
				String s2 = JOptionPane.showInputDialog("Enter Final State");

				if (s2 == null)
					end = -1;
				else
					end = hash.get(s2);
			} while (end == null);

			if (end >= 0)
			{
				// valid input solve the problem
				ArrayList<String> res = tree.solveSimpleControl(start, end);

				if (res.size() == 0)
				{
					noSolution(); // NO SOLUTION was FOUND
					return;
				}

				String[] hamada = new String[res.size()];
				res.toArray(hamada);

				// TODO Show output
				ArrayList<String[][]> table = new ArrayList<String[][]>();
				for (int i = 0; i < res.size(); i++)
				{
					table.add(new String[][] { { states[start], states[end] } });
				}

				outFrame = new OutputFrame(hamada, table,
						"Simple control problem");
				outFrame.setVisible(true);
			}
		}
	}

	private void solveTerminal() throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
	{
		boolean valid = readInput();
		if (!valid)
		{
			JOptionPane.showMessageDialog(null, "Invalid Number of States",
					"Error", JOptionPane.ERROR_MESSAGE);
			return;
		}
		TreeBuilder tree = new TreeBuilder(transitionTable, output);
		ArrayList<String> sequences = tree.solveTerminal();

		if (sequences.size() == 0)
		{
			noSolution(); // NO SOLUTION was FOUND
			return;
		}

		String[] seqs = new String[sequences.size()];
		sequences.toArray(seqs);

		ArrayList<String[][]> tables = tree.getResultTable();
		// TODO Show Output

		// will parse the following table to get the states real name instead of
		// integers
		for (String[][] table : tables)
		{
			for (int i = 0; i < table.length; i++)
			{
				int pos = Integer.parseInt(table[i][0]);
				table[i] = new String[] { states[pos], table[i][1] };
			}
		}
		outFrame = new OutputFrame(seqs, tables,
				"Terminal State identification problem");
		outFrame.setVisible(true);
	}

	private void solveSynch() throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
	{
		boolean valid = readInput();
		if (!valid)
		{
			JOptionPane.showMessageDialog(null, "Invalid Number of States",
					"Error", JOptionPane.ERROR_MESSAGE);
			return;
		}

		TreeBuilder tree = new TreeBuilder(transitionTable, output);
		ArrayList<String> sequences = tree.solveSynchronization();
		ArrayList<String[][]> tables = tree.getResultTable();

		if (sequences.size() == 0)
		{
			noSolution(); // NO SOLUTION was FOUND
			return;
		}

		String[] seqs = new String[sequences.size()];
		sequences.toArray(seqs);

		// will parse the following table to get the states real name instead of
		// integers
		for (String[][] table : tables)
		{
			for (int i = 0; i < table.length; i++)
			{
				int state1 = Integer.parseInt(table[i][0]);
				int state2 = Integer.parseInt(table[i][1]);
				table[i] = new String[] { states[state1], states[state2] };
			}
		}

		// TODO Show output
		outFrame = new OutputFrame(seqs, tables,
				"Terminal synchronization state problem");
		outFrame.setVisible(true);
	}

	private void noSolution()
	{
		JOptionPane.showMessageDialog(null, "Can't Find a solution", "Error",
				JOptionPane.ERROR_MESSAGE);
	}

	private void solveGeneralControl() throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
	{
		boolean valid = readInput();
		if (!valid)
		{
			JOptionPane.showMessageDialog(null, "Invalid Number of States",
					"Error", JOptionPane.ERROR_MESSAGE);
			return;
		}
		Integer finalState = null;
		do
		{
			String state = JOptionPane.showInputDialog("Enter Final State");
			
			if (state == null)
				finalState = -1;
			else
				finalState = hash.get(state);
		} while (finalState == null);

		if (finalState >= 0)
		{
			GeneralControlSolver solver = new GeneralControlSolver(
					transitionTable, output);
			String[] sequences = solver.solve(finalState);
			ArrayList<String[][]> tables = solver.getTables();

			if (sequences.length == 0)
			{
				noSolution(); // NO SOLUTION was FOUND
				return;
			}
			
			// will parse the following table to get the states real name instead of
			// integers
			for (String[][] table : tables)
			{
				for (int i = 0; i < table.length; i++)
				{
					int state1 = Integer.parseInt(table[i][0]);
					int state2 = Integer.parseInt(table[i][3]);
					table[i][0] = states[state1];
					table[i][3] = "=> "+states[state2];
				}
			}
			
			// TODO Show output
			outFrame = new OutputFrame(sequences, tables, "General problem");
			outFrame.setVisible(true);
		}
	}

}
